﻿using System;
using System.ComponentModel.Composition;
using System.Linq;
using System.Xml.Linq;
using DocumentFormat.OpenXml.Packaging;
using OpenXmlPowerTools;

namespace Songhay.OpenXml.Transformations
{
    using Songhay.OpenXml.Extensions;
    using Songhay.OpenXml.Models;
    using System.Collections.Generic;

    /// <summary>
    /// Transforms the Open XML WordML <c>p</c> element.
    /// </summary>
    [Export(typeof(IWordProcessingDocumentTransformation))]
    public class ParagraphTransformation : IWordProcessingDocumentTransformation
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="ParagraphTransformation"/> class.
        /// </summary>
        public ParagraphTransformation()
        {
            this.ExecutionOrdinal = 1;
        }

        #region IWordProcessingDocumentTransform Members

        /// <summary>
        /// Gets or sets the execution ordinal.
        /// </summary>
        /// <value>The execution ordinal.</value>
        public byte ExecutionOrdinal { get; set; }

        public bool CanTransform(WordprocessingDocument wordDoc,
            SonghayHtmlConverterSettings settings, XNode node)
        {
            if(!this.HasElementName(node, SonghayW.p)) return false;

            var element = node as XElement;
            if(element == null) return false;

            var styleName = "BlockText";
            if(IsParagraphStyle(element, styleName))
                return IsFirstParagraphOfStyle(element, styleName);

            styleName = "ListBullet";
            if(IsParagraphStyle(element, styleName))
                return IsFirstParagraphOfStyle(element, styleName);

            styleName = "ListParagraph";
            if(IsParagraphStyle(element, styleName))
                return IsFirstParagraphOfStyle(element, styleName);

            return true;
        }

        public object Transform(WordprocessingDocument wordDoc,
            SonghayHtmlConverterSettings settings, XNode node,
            Func<ImageInfo, XElement> imageHandler)
        {
            var element = node as XElement;

            //Support non-p blocks:
            if(GetStyleId(element) == "HTMLPreformatted")
            {
                var pre = new XElement(SonghayXhtml.pre,
                    new XAttribute(XNamespace.Xml + "space", "preserve"));
                var preValue = "\n";
                element.Descendants(SonghayW.t)
                    .ToList()
                    .ForEach(t => preValue += t.Value + "\n");
                pre.Value = preValue;
                return pre;
            }

            //Get paragraph:
            XElement p = GetParagraph(wordDoc, settings, imageHandler, element);

            //Support selected paragraph styles:
            var styleName = string.Empty;

            styleName = "BlockText";
            if(IsParagraphStyle(element, styleName))
            {
                var blockquote = new XElement(SonghayXhtml.blockquote);

                var li = new XElement(SonghayXhtml.li);
                blockquote.Add(p);
                foreach(var item in element.ElementsAfterSelf(SonghayW.p).ToList())
                {
                    if(!IsParagraphStyle(item, styleName)) break;
                    var pNext = GetParagraph(wordDoc, settings, imageHandler, item);
                    blockquote.Add(pNext);
                }

                return blockquote;
            }

            #region supported list styles:

            var addListItems = new Action<string, XElement, XElement>(
                (listStyleName, firstListItem, htmlList) =>
                {
                    foreach(var item in firstListItem
                        .ElementsAfterSelf(SonghayW.p).ToList())
                    {
                        if(!IsParagraphStyle(item, listStyleName)) break;
                        var pNext = GetParagraph(wordDoc, settings, imageHandler, item);
                        htmlList.Add(new XElement(SonghayXhtml.li, pNext.Nodes()));
                    }
                }
            );

            styleName = "ListBullet";
            if(IsParagraphStyle(element, styleName))
            {
                var ul = new XElement(SonghayXhtml.ul);

                var li = new XElement(SonghayXhtml.li);
                li.Add(p.Nodes());
                ul.Add(li);
                addListItems(styleName, element, ul);

                return ul;
            }

            styleName = "ListParagraph";
            if(IsParagraphStyle(element, styleName))
            {
                var ol = new XElement(SonghayXhtml.ol);

                var li = new XElement(SonghayXhtml.li);
                li.Add(p.Nodes());
                ol.Add(li);
                addListItems(styleName, element, ol);

                return ol;
            }

            #endregion

            return OpenXmlUtility.RenderEmptyElement(p);
        }

        #endregion

        static XAttribute GetCssClassAttribute(WordprocessingDocument wordDoc,
            SonghayHtmlConverterSettings settings, XElement element)
        {
            string styleId = GetStyleId(element);
            if(styleId == null)
            {
                styleId = (string)wordDoc.MainDocumentPart.StyleDefinitionsPart
                    .GetXDocument().Root.Elements(SonghayW.style)
                    .Where(e => (string)e.Attribute(SonghayW.type) == "paragraph" &&
                       (string)e.Attribute(SonghayW.@default) == "1")
                    .FirstOrDefault().Attributes(SonghayW.styleId).FirstOrDefault();
            }
            return styleId != null ? (
                settings.CssClassPrefix != null ?
                new XAttribute(SonghayXhtml.@class,
                    settings.CssClassPrefix + styleId) : null
            ) : null;
        }

        static XElement GetParagraph(WordprocessingDocument wordDoc,
            SonghayHtmlConverterSettings settings,
            Func<ImageInfo, XElement> imageHandler, XElement element)
        {
            XElement p = new XElement(SonghayXhtml.p,
                GetCssClassAttribute(wordDoc, settings, element),
                OpenXmlUtility.TranslateEntities(element.Elements(SonghayW.t)
                    .Select(s => (string)s).StringConcatenate(), settings),
                element.Elements().Select(e => OpenXmlUtility.TransformPackageNode(wordDoc,
                    settings, e, imageHandler)));
            return p;
        }

        static string GetStyleId(XElement element)
        {
            var id = (string)element.Elements(SonghayW.pPr).Elements(SonghayW.pStyle)
                .Attributes(SonghayW.val).FirstOrDefault();
            return id;
        }

        static bool IsFirstParagraphOfStyle(XElement element, string styleName)
        {
            if(!IsParagraphStyle(element, styleName)) return false;

            return !IsParagraphStyle(element
                .ElementsBeforeSelfReverseDocumentOrder().FirstOrDefault(), styleName);
        }

        static bool IsParagraphStyle(XElement element, string styleName)
        {
            if(element == null) return false;
            return element.Elements(SonghayW.pPr)
                .Elements(SonghayW.pStyle)
                .Attributes(SonghayW.val)
                .Where(a => a.Value == styleName).Count() > 0;
        }
    }
}
